// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/

import Common
import UIKit

/// The view model used to configure a `CollapsibleCardView`
public struct CollapsibleCardViewModel {
    public typealias ExpandState = CollapsibleCardView.ExpandButtonState

    public let contentView: UIView
    public let cardViewA11yId: String

    public let title: String
    public let titleA11yId: String

    public let expandButtonA11yId: String
    public let expandButtonA11yLabelExpand: String
    public let expandButtonA11yLabelCollapse: String

    public var expandState: ExpandState = .collapsed

    public var expandButtonA11yLabel: String {
        return expandState == .expanded ? expandButtonA11yLabelCollapse : expandButtonA11yLabelExpand
    }

    public var telemetryCallback: ((_ expandState: ExpandState) -> Void)?

    // We need this init as by default the init generated by the compiler for the struct will be internal and
    // can therefor not be used outside of the component library
    public init(contentView: UIView,
                cardViewA11yId: String,
                title: String,
                titleA11yId: String,
                expandButtonA11yId: String,
                expandButtonA11yLabelExpand: String,
                expandButtonA11yLabelCollapse: String,
                expandState: ExpandState = .collapsed,
                telemetryCallback: ((_ expandState: ExpandState) -> Void)? = nil) {
        self.contentView = contentView
        self.cardViewA11yId = cardViewA11yId
        self.title = title
        self.titleA11yId = titleA11yId
        self.expandButtonA11yId = expandButtonA11yId
        self.expandButtonA11yLabelExpand = expandButtonA11yLabelExpand
        self.expandButtonA11yLabelCollapse = expandButtonA11yLabelCollapse
        self.expandState = expandState
        self.telemetryCallback = telemetryCallback
    }
}

public final class CollapsibleCardView: ShadowCardView, UIGestureRecognizerDelegate {
    private struct UX {
        static let verticalPadding: CGFloat = 8
        static let horizontalPadding: CGFloat = 8
        static let titleHorizontalPadding: CGFloat = 8
        static let expandButtonSize = CGSize(width: 20, height: 20)
        static let margins = UIEdgeInsets(top: 8, left: 0, bottom: 8, right: 0)
    }

    public enum ExpandButtonState {
        case collapsed
        case expanded

        var image: UIImage? {
            switch self {
            case .expanded:
                return UIImage(named: StandardImageIdentifiers.Large.chevronUp)?.withRenderingMode(.alwaysTemplate)
            case .collapsed:
                return UIImage(named: StandardImageIdentifiers.Large.chevronDown)?.withRenderingMode(.alwaysTemplate)
            }
        }

        var toggle: ExpandButtonState {
            switch self {
            case .expanded:
                return .collapsed
            case .collapsed:
                return .expanded
            }
        }
    }

    // MARK: - Properties
    private lazy var viewModel = CollapsibleCardViewModel(
        contentView: rootView,
        cardViewA11yId: "",
        title: "",
        titleA11yId: "",
        expandButtonA11yId: "",
        expandButtonA11yLabelExpand: "",
        expandButtonA11yLabelCollapse: "",
        expandState: .collapsed)

    // UI
    private lazy var rootView: UIStackView = .build { stackView in
        stackView.axis = .vertical
        stackView.spacing = UX.verticalPadding
        stackView.alignment = .center
        stackView.isLayoutMarginsRelativeArrangement = true
        stackView.layoutMargins = UX.margins
    }

    private lazy var headerView: UIView = .build { _ in }
    private lazy var containerView: UIView = .build { _ in }
    private var tapRecognizer: UITapGestureRecognizer?

    lazy var titleLabel: UILabel = .build { label in
        label.adjustsFontForContentSizeCategory = true
        label.font = FXFontStyles.Bold.subheadline.scaledFont()
        label.numberOfLines = 0
        label.accessibilityTraits.insert(.header)
    }

    private lazy var expandButton: UIButton = .build { view in
        view.setImage(self.viewModel.expandState.image, for: .normal)
        view.addTarget(self, action: #selector(self.toggleExpand), for: .touchUpInside)
    }

    // MARK: - Inits
    override init(frame: CGRect) {
        super.init(frame: frame)

        setupLayout()

        let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapHeader))
        tapRecognizer.delegate = self
        headerView.addGestureRecognizer(tapRecognizer)

        self.tapRecognizer = tapRecognizer
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override public func configure(_ viewModel: ShadowCardViewModel) {
        // the overridden method should not be used as it is lacking vital details to configure this card
        fatalError("configure(:) has not been implemented.")
    }

    public func configure(_ viewModel: CollapsibleCardViewModel) {
        self.viewModel = viewModel
        containerView.subviews.forEach { $0.removeFromSuperview() }
        containerView.addSubview(viewModel.contentView)

        titleLabel.text = viewModel.title
        titleLabel.accessibilityIdentifier = viewModel.titleA11yId
        expandButton.accessibilityIdentifier = viewModel.expandButtonA11yId
        expandButton.accessibilityLabel = viewModel.expandButtonA11yLabel

        NSLayoutConstraint.activate([
            viewModel.contentView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
            viewModel.contentView.topAnchor.constraint(equalTo: containerView.topAnchor),
            viewModel.contentView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
            viewModel.contentView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
        ])

        updateCardState(expandState: viewModel.expandState)

        let parentViewModel = ShadowCardViewModel(view: rootView, a11yId: viewModel.cardViewA11yId)
        super.configure(parentViewModel)
    }

    override public func applyTheme(theme: Theme) {
        super.applyTheme(theme: theme)

        titleLabel.textColor = theme.colors.textPrimary
        expandButton.tintColor = theme.colors.iconPrimary
    }

    private func setupLayout() {
        configure(viewModel)

        headerView.addSubview(titleLabel)
        headerView.addSubview(expandButton)
        rootView.addArrangedSubview(headerView)
        rootView.addArrangedSubview(containerView)

        NSLayoutConstraint.activate([
            headerView.leadingAnchor.constraint(equalTo: rootView.leadingAnchor,
                                                constant: UX.titleHorizontalPadding),
            headerView.trailingAnchor.constraint(equalTo: rootView.trailingAnchor,
                                                 constant: -UX.titleHorizontalPadding),
            titleLabel.leadingAnchor.constraint(equalTo: headerView.leadingAnchor),
            titleLabel.topAnchor.constraint(equalTo: headerView.topAnchor),
            titleLabel.trailingAnchor.constraint(equalTo: expandButton.leadingAnchor,
                                                 constant: -UX.horizontalPadding),
            titleLabel.bottomAnchor.constraint(equalTo: headerView.bottomAnchor),
            titleLabel.heightAnchor.constraint(greaterThanOrEqualToConstant: UX.expandButtonSize.height),

            expandButton.topAnchor.constraint(greaterThanOrEqualTo: headerView.topAnchor),
            expandButton.trailingAnchor.constraint(equalTo: headerView.trailingAnchor),
            expandButton.bottomAnchor.constraint(lessThanOrEqualTo: headerView.bottomAnchor),
            expandButton.centerYAnchor.constraint(equalTo: titleLabel.centerYAnchor),
            expandButton.widthAnchor.constraint(equalToConstant: UX.expandButtonSize.width),
            expandButton.heightAnchor.constraint(equalToConstant: UX.expandButtonSize.height),

            containerView.leadingAnchor.constraint(equalTo: rootView.leadingAnchor,
                                                   constant: UX.horizontalPadding),
            containerView.trailingAnchor.constraint(equalTo: rootView.trailingAnchor,
                                                    constant: -UX.horizontalPadding),
        ])
    }

    private func updateCardState(expandState: ExpandButtonState) {
        let isCollapsed = expandState == .collapsed
        viewModel.expandState = expandState
        expandButton.setImage(viewModel.expandState.image, for: .normal)
        expandButton.accessibilityLabel = viewModel.expandButtonA11yLabel
        containerView.isHidden = isCollapsed
        UIAccessibility.post(notification: .layoutChanged, argument: nil)
    }

    @objc
    private func toggleExpand(_ sender: UIButton) {
        updateCardState(expandState: viewModel.expandState.toggle)
        viewModel.telemetryCallback?(viewModel.expandState)
    }

    @objc
    func tapHeader(_ recognizer: UITapGestureRecognizer) {
        updateCardState(expandState: viewModel.expandState.toggle)
        viewModel.telemetryCallback?(viewModel.expandState)
    }
}
